﻿// -------------------------------------------------------------------------------------------------------
// <copyright file="CDSTools.cs" company="www.swyx-forum.com">
//     (c) Martin Hüser
// </copyright>
// <summary>Defines CDSTools type which wraps ConfigDataStore API and provides utility methods.</summary>
// -------------------------------------------------------------------------------------------------------
namespace IpPbxImportRR
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using SWConfigDataClientLib;
    using SWConfigDataClientLib.Proxies.PortBundle;
    using SWConfigDataClientLib.Proxies;
    using SWConfigDataClientLib.Data;
using SWConfigDataClientLib.Proxies.Admin;

    /// <summary>
    /// CDSTools type wrapping ConfigDataStore API and providing utility methods.
    /// </summary>
    internal class CDSTools : IDisposable
    {
        /// <summary>
        /// flag indicating if CDS connection is established
        /// </summary>
        private bool cdsConnected;

        /// <summary>
        /// CDS lib manager instance. Root of CDS client API types
        /// </summary>
        private LibManager libManager;

        /// <summary>
        /// CDS trunk group enumerator
        /// </summary>
        private PortBundleEnum trunkGroupEnum;

        /// <summary>
        /// read-only routing table
        /// </summary>
        private RoutingTableAdminView1PrimaryCollection routingTableView;

        /// <summary>
        /// lookup table for userid of given name
        /// </summary>
        private Dictionary<string, int> userNames;

        /// <summary>
        /// lookup table for groupIDs of given name
        /// </summary>
        private Dictionary<string, int> groupNames;

        /// <summary>
        /// lookup table for locationIDs of given name
        /// </summary>
        private Dictionary<string, int> locationNames;

        /// <summary>
        /// lookup table for trunkGroupIDs of given name
        /// </summary>
        private Dictionary<string, int> trunkGroupNames;

        /// <summary>
        /// lookup table for user names of given id
        /// </summary>
        private Dictionary<int, string> userIDs;

        /// <summary>
        /// lookup table for group names of given id
        /// </summary>
        private Dictionary<int, string> groupIDs;

        /// <summary>
        /// lookup table for location names of given id
        /// </summary>
        private Dictionary<int, string> locationIDs;

        /// <summary>
        /// lookup table for trunk names of given id
        /// </summary>
        private Dictionary<int, string> trunkGroupIDs;

        /// <summary>
        /// flag to indicate if this.Dispose() has been explicitely called
        /// </summary>
        private bool disposed = false;

        /// <summary>
        /// Gets CDS trunk group enumerator
        /// </summary>
        /// <remarks>Enum is refreshed on every get, i.e. retrieves data from CDS</remarks>
        public PortBundleEnum TrunkGroupEnum
        {
            get
            {
                if (this.trunkGroupEnum == null)
                {
                    this.trunkGroupEnum = this.libManager.GetPortBundleEnum();
                    OrderByList orderBy = new SWConfigDataClientLib.Data.OrderByList();
                    orderBy.Add(new OrderBy("Name", true));
                    this.trunkGroupEnum.ExecuteAllFilter(orderBy);
                }

                return this.trunkGroupEnum;
            }
        }

        /// <summary>
        /// Gets read-only routing table
        /// </summary>
        public RoutingTableAdminView1PrimaryCollection RoutingTableView
        {
            get
            {
                return this.routingTableView;
            }
        }

        /// <summary>
        /// Called to dispose unmanaged resources. If you do not call it explicitely, finalizer
        /// will do it at an arbitrary time
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);

            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }
        
        /// <summary>
        /// Connect to ConfigDataStore
        /// </summary>
        /// <param name="servername">servername to connect to. Connects to localhost when empty</param>
        /// <param name="username">username to user for authentication</param>
        /// <param name="password">password to user for authentication</param>
        internal void ConnectToCDS(string servername, string username, string password)
        {
            if (this.cdsConnected)
            {
                return;
            }

            if (this.libManager == null)
            {
                this.libManager = new LibManager();
            }

            if (!string.IsNullOrEmpty(username))
            {
                this.libManager.Username = "ippbx:" + username;
                this.libManager.Password = password;
                this.libManager.AuthenticationMode = SProxyObject.AuthenticationMode.UsernamePassword;
            }
            else
            {
                if (string.IsNullOrEmpty(servername))
                {
                    this.libManager.AuthenticationMode = SProxyObject.AuthenticationMode.TrustedLocal;
                }
                else
                {
                    this.libManager.AuthenticationMode = SProxyObject.AuthenticationMode.Trusted;
                }
            }

            this.libManager.BaseProtocol = SProxyObject.BaseProtocol.TCP;

            if (servername != null)
            {
                this.libManager.WSBaseUrl = servername;
            }
            else
            {
                this.libManager.WSBaseUrl = "127.0.0.1";
            }

            // create lookup tables for userids, groupids and location ids
            PortBundleEnum trunkGroups = this.TrunkGroupEnum;
            ValueListCollection vlc = trunkGroups.getValueLists(SWConfigDataClientLib.Proxies.PortBundle.ValueLists.ALL);

            // create loopup tables
            this.userNames = new Dictionary<string, int>();
            this.userIDs = new Dictionary<int, string>();
            foreach (ValueItem item in vlc["UserID"])
            {
                this.userNames.Add(item.Text.ToUpperInvariant(), item.KeyInt);
                this.userIDs.Add(item.KeyInt, item.Text);
            }

            this.groupNames = new Dictionary<string, int>();
            this.groupIDs = new Dictionary<int, string>();
            foreach (ValueItem item in vlc["GroupID"])
            {
                this.groupNames.Add(item.Text.ToUpperInvariant(), item.KeyInt);
                this.groupIDs.Add(item.KeyInt, item.Text);
            }

            this.locationNames = new Dictionary<string, int>();
            this.locationIDs = new Dictionary<int, string>();
            foreach (ValueItem item in vlc["LocationID"])
            {
                this.locationNames.Add(item.Text.ToUpperInvariant(), item.KeyInt);
                this.locationIDs.Add(item.KeyInt, item.Text);
            }

            this.trunkGroupNames = new Dictionary<string, int>();
            this.trunkGroupIDs = new Dictionary<int, string>();
            
            // there is no PortBundleID value list, we need to retrieve the values from the enum
            foreach (PortBundleEntry trunkGroup in trunkGroups.PrimaryCollection)
            {
                this.trunkGroupNames.Add(trunkGroup.Name.ToUpperInvariant(), trunkGroup.PortBundleID);
                this.trunkGroupIDs.Add(trunkGroup.PortBundleID, trunkGroup.Name.ToUpperInvariant());
            }

            AdminFacade adminFacade = this.libManager.GetAdminFacade();
            this.routingTableView = adminFacade.GetRoutingTableAdminView1();

            this.cdsConnected = true;
        }

        /// <summary>
        /// Disconnect from ConfigDataStore.
        /// </summary>
        /// <returns>true if successfully disconnected, false if no connection exists</returns>
        internal bool DisconnectFromCDS()
        {
            if (!this.cdsConnected)
            {
                return false;
            }

            if (this.trunkGroupEnum != null)
            {
                this.trunkGroupEnum = null;
            }

            if (this.libManager != null)
            {
                this.libManager = null;
            }

            return true;
        }

        /// <summary>
        /// Looks up userID of given user name
        /// </summary>
        /// <param name="name">Name of user to look up. Case-insensitive</param>
        /// <returns>UserID or zero if user could not be found</returns>
        internal int LookupUserID(string name)
        {
            string lowerCaseName = name.ToUpperInvariant();
            if (this.userNames.ContainsKey(lowerCaseName))
            {
                return this.userNames[lowerCaseName];
            }
            else
            {
                return 0;
            }
        }

        /// <summary>
        /// Looks up groupID of given group name
        /// </summary>
        /// <param name="name">Name of group to look up. Case-insensitive</param>
        /// <returns>Group id or zero if user could not be found</returns>
        internal int LookupGroupID(string name)
        {
            string lowerCaseName = name.ToUpperInvariant();
            if (this.groupNames.ContainsKey(lowerCaseName))
            {
                return this.groupNames[lowerCaseName];
            }
            else
            {
                return 0;
            }
        }

        /// <summary>
        /// Looks up locationID of given location name
        /// </summary>
        /// <param name="name">Name of location to look up. Case-insensitive</param>
        /// <returns>Location id or zero if user could not be found</returns>
        internal int LookupLocationID(string name)
        {
            string lowerCaseName = name.ToUpperInvariant();
            if (this.locationNames.ContainsKey(lowerCaseName))
            {
                return this.locationNames[lowerCaseName];
            }
            else
            {
                return 0;
            }
        }

        /// <summary>
        /// Looks up trunkgroupID of given trunkgroup name
        /// </summary>
        /// <param name="name">Name of trunk group to look up. Case-insensitive</param>
        /// <returns>Trunk group id or zero if user could not be found</returns>
        internal int LookupTrunkGroupID(string name)
        {
            string lowerCaseName = name.ToUpperInvariant();
            if (this.trunkGroupNames.ContainsKey(lowerCaseName))
            {
                return this.trunkGroupNames[lowerCaseName];
            }
            else
            {
                return 0;
            }
        }

        /// <summary>
        /// Looks up username for given id
        /// </summary>
        /// <param name="id">user id to look up. </param>
        /// <returns>username or empty if user not found</returns>
        internal string LookupUserName(int id)
        {
            if (this.userIDs.ContainsKey(id))
            {
                return this.userIDs[id];
            }
            else
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// Looks up groupname for given id
        /// </summary>
        /// <param name="id">group id to look up. </param>
        /// <returns>username or empty if group not found</returns>
        internal string LookupGroupName(int id)
        {
            if (this.groupIDs.ContainsKey(id))
            {
                return this.groupIDs[id];
            }
            else
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// Looks up location name for given id
        /// </summary>
        /// <param name="id">location id to look up. </param>
        /// <returns>username or empty if location not found</returns>
        internal string LookupLocationName(int id)
        {
            if (this.locationIDs.ContainsKey(id))
            {
                return this.locationIDs[id];
            }
            else
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// Looks up trunkGroup name for given id
        /// </summary>
        /// <param name="id">trunkGroup id to look up. </param>
        /// <returns>trunk group name or empty if trunkGroup not found</returns>
        internal string LookupTrunkGroupName(int id)
        {
            if (this.trunkGroupIDs.ContainsKey(id))
            {
                return this.trunkGroupIDs[id];
            }
            else
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// Dispose(bool disposing) executes in two distinct scenarios.
        /// </summary>
        /// <param name="disposing">If disposing equals true, the method has been called directly
        /// or indirectly by a user's code. Managed and unmanaged resources
        /// can be disposed.
        /// If disposing equals false, the method has been called by the
        /// runtime from inside the finalizer and you should not reference
        /// other objects. Only unmanaged resources can be disposed.</param>
        private void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this.disposed)
            {
                // If disposing equals true, dispose all managed
                // and unmanaged resources.
                if (disposing)
                {
                    // Dispose managed resources.
                    this.trunkGroupEnum = null;
                    this.userIDs = null;
                    this.userNames = null;
                    this.groupIDs = null;
                    this.groupNames = null;
                    this.locationIDs = null;
                    this.locationNames = null;
                    this.trunkGroupEnum = null;
                    this.libManager.Dispose();
                    this.libManager = null;
                }

                // Note disposing has been done.
                this.disposed = true;
            }
        }
    }
}
